/* 
 * File:   Nave.cpp
 * Author: cesar
 * 
 * Implementacion de la Clase Nave.hpp y sus métodos.
 * 
 */

#include "Nave.hpp"
#define SHOOT_RATE 40 //cantidad de frames que deben pasar antes de otro disparo
#define DISAPEAR_TIME 6 //tiempo de desaparicion de la nave
#define RECUPERATION_TIME 60 //tiempo de invulnerabilidad.

/**
 * Constructor de la clase Nave.
 * 
 * @param scalado : La escala a aplicar al modelo que representa la nave.
 * @param nombre : Nombre del modelo de la Nave.
 * @param maxObj : Cantidad de Objetivos.
 */
Nave::Nave(GLfloat scalado, char* nombre, int maxObj) : Modelo(scalado, nombre, 0.0, 0.0, 0.0) {
    maxObjetivos = maxObj;
    proxObj = 0;
    GLenum luces[] = {GL_LIGHT1, GL_LIGHT2, GL_LIGHT3};

    //inicializar los lasers de la nave
    for (int i = 0; i < 3; i++) {
        disparos.push_back(Laser(luces[i], x, y, z));
    }
    //eliminar emision de luz
    material_emision[0] = 0.0;
    material_emision[1] = 0.0;
    material_emision[2] = 0.0;
    reload = SHOOT_RATE;
    impactada = false;
    contador_impacto = 0;
    angulo=0.0;
    angulo_fb=0.0;



}

/**
 * Este metodo permite dispara un laser de la lista de disparos
 */
void Nave::disparar() {

    if (!disparos.front().activado() && reload >= SHOOT_RATE) {
        disparos.front().activar();
        disparos.front().setInitialPosition(x, y, z);
        //disparos.front().setTarget(x,y,z-1);


        disparos.push_back(disparos.front());
        disparos.pop_front();
        reload = 0;
    }

}

/**
 * Este metodo permite dispara un laser de la lista de disparos.
 * Ademas define la direccion a la que debe ir el disparo.
 * 
 * @param x2 : Posicion en el eje X del objetivo.
 * @param y2 : Posicion en el eje Y del objetivo.
 * @param z2 : Posicion en el eje Z del objetivo.
 */
void Nave::disparar(GLfloat x2, GLfloat y2, GLfloat z2) {

    if (!disparos.front().activado() && reload >= SHOOT_RATE) {
        disparos.front().activar();
        disparos.front().setInitialPosition(x, y, z);
        disparos.front().setTarget(x2, y2, z2);

        disparos.push_back(disparos.front());
        disparos.pop_front();
        reload = 0;
    }

}

/**
 * Este metodo permite dibujar la nave en la escena.
 */
void Nave::dibujar() {

    GLfloat position[] = {0.0, 0.0, z + 5.0, 0.0};
    //se coloca la camara atras de la nave
    gluLookAt(0, 0, z + 30.0, 0.0, 0.0, 0, 0.0, 1.0, 0.0);
    //se coloca una luz atras de la nave
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    if (contador_impacto % DISAPEAR_TIME == 0) {
        
        /**********DIBUJAR LA SOMBRA****************/
        glPushMatrix();
        
        activarModoSombra();

        glTranslatef(x, y, z - 60);
        glScalef(escala - y * 0.10, escala - y * 0.10, escala - y * 0.10);
        glRotatef(-angulo,0.0,0.0,1.0);
        glRotatef(angulo_fb,1.0,0.0,0.0);
        glmDraw(model, GLM_SMOOTH);

        desactivarModoSombra();
        glPopMatrix();
        
        /**********DIBUJAR LA NAVE************/
        glPushMatrix();


        glTranslatef(x, y, z);

        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emision);

        glScalef(escala, escala, escala);
        glRotatef(angulo,0.0,0.0,1.0);
        glRotatef(angulo_fb,1.0,0.0,0.0);

        glmDraw(model, GLM_MATERIAL | GLM_SMOOTH);







        glPopMatrix();
    }
    //se detecta si la nave fue impactada y se le da un periodo de invulnerabilidad
    if (contador_impacto > 0) {
        contador_impacto--;
    } else {
        impactada = false;
        material_emision[0] = 0.0;
    }
    //dibujar los lasers
    dibujarLasers();

}

/**
 * Este metodo permite dibujar los lasers en la escena.
*/
void Nave::dibujarLasers() {

    list<Laser>::iterator it = disparos.begin();

    while (it != disparos.end()) {

        (*it).dibujar();
        it++;

    }


}


/**Este metodo permite mover la nave a la derecha*/
void Nave::moverDer() {
    if (x < ancho_pantalla) {
        x += 0.3;
        
        
        
        if(angulo<270.0){
            angulo+=1;
        }
        
    }
}

/**
 * Este metodo permite mover la nave a la izquierda
 */
void Nave::moverIzq() {
    if (x>-ancho_pantalla) {
        x -= 0.3;
        
         
        
        if(angulo>-270){
            angulo-=1;
        }
        
    }
}

/**
 * Este metodo permite mover la nave hacia arriba.
 */
void Nave::moverArriba() {
    if (y < alto_pantalla) {
        y += 0.3;
        
        
        if(angulo_fb<270.0){
            angulo_fb+=1;
        }
    }
}

/**
 * Este metodo permite mover la nave hacia abajo.
 */
void Nave::moverAbajo() {
    if (y >= -alto_pantalla) {
        y -= 0.3;
        
        if(angulo_fb>-270){
            angulo_fb-=1;
        }
    }
}



/**
 * Esta funcion permite obtener la puntuacion del usuario.
 * @return La puntuacion del usuario.
 */
int Nave::getScore() {
    return score;
}

/**
 * Este metodo permite anadir puntos a la puntuacion del jugador.
 * @param s : La cantidad de puntos a sumar al puntaje actual.
 */
void Nave::addScore(int s) {

    score += s;
}


/**
 * Este metodo permite deinir los limites de movimiento de la nave en X y Y.
 * 
 * @param w : El limite de ancho de la pantalla.
 * @param h : El limite de alto de la pantalla.
 */
void Nave::setLimits(int w, int h) {
    ancho_pantalla = w;
    alto_pantalla = h;

}


/**
 * Este metodo permite realizar los calculos de la tasa de disparos,
 * y ejecuta el metodo idle de cada disparo.
 * 
 * @param s : El frameskip actual. 
 */
void Nave::idle(int s) {/* Default s = 1*/
    if (reload < SHOOT_RATE) {
        reload += s;
    }
    
    if(angulo<0){
        angulo+=0.5;
    
    }else{
        
        if(angulo>0){
            angulo-=0.5;
        
        }
    
    
    }
    
    if(angulo_fb<0){
        angulo_fb+=0.5;
    
    }else{
        
        if(angulo_fb>0){
            angulo_fb-=0.5;
        
        }
    
    
    }

    list<Laser>::iterator it = disparos.begin();
    while (it != disparos.end()) {
        (*it).idle(s);
        it++;
    }
}



/**
 * Este metodo le indica a la nave que ha pasado al objetivo mas cercano 
 * Por ese motivo avanza 1 en el apuntador del arreglo que representa
 * un toroide.
 */
void Nave::avanzarObjetivo() {
    proxObj++;
    proxObj %= maxObjetivos;
}

/**
 * Indica que la nave fue impactada y necesita un tiempo de invulnerabilidad.
 */
void Nave::setImpactada() {
    material_emision[0] = 1.0;
    impactada = true;
    contador_impacto = RECUPERATION_TIME;

}

/**
 * Indica si la nave esta en su tiempo de recuperacion o no.
 * @return true si fue impactada false en caso contrario.
 */
bool Nave::getImpactada() {
    return impactada;
}